package com.ryder.petmatediarybackend.common.util;

import jakarta.servlet.http.HttpServletRequest;
import org.lionsoul.ip2region.xdb.Searcher;

import java.io.InputStream;
import java.util.Objects;

/**
 * 描述: IP 工具类
 *
 * @author Ryder
 * @version 1.0
 * @since 1.0
 * 更新日期: 2025/10/25
 */
public class IpUtil {

    private static final Searcher searcher;

    static {
        try {
            // 从 classpath 加载 ip2region.xdb
            InputStream is = Objects.requireNonNull(
                    IpUtil.class.getClassLoader().getResourceAsStream("ip2region/ip2region.xdb")
            );
            byte[] cBuff = is.readAllBytes();
            searcher = Searcher.newWithBuffer(cBuff);
        } catch (Exception e) {
            throw new RuntimeException("加载 ip2region.xdb 失败", e);
        }
    }

    /**
     * 获取客户端真实 IP
     * 优先级：X-Forwarded-For > X-Real-IP > RemoteAddr
     * 自动处理 IPv6 localhost 转换为 IPv4
     */
    public static String getClientIp(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (ip != null && !ip.isBlank() && !"unknown".equalsIgnoreCase(ip)) {
            ip = ip.split(",")[0].trim();
            return normalizeIp(ip);
        }
        
        ip = request.getHeader("X-Real-IP");
        if (ip != null && !ip.isBlank() && !"unknown".equalsIgnoreCase(ip)) {
            return normalizeIp(ip);
        }
        
        ip = request.getRemoteAddr();
        return normalizeIp(ip);
    }

    /**
     * 规范化 IP 地址
     * 将 IPv6 的 localhost 转换为 IPv4 格式
     */
    private static String normalizeIp(String ip) {
        if (ip == null || ip.isBlank()) {
            return "unknown";
        }
        
        // IPv6 localhost 转换为 IPv4
        if ("0:0:0:0:0:0:0:1".equals(ip) || "::1".equals(ip)) {
            return "127.0.0.1";
        }
        
        return ip;
    }

    /**
     * 根据 IP 获取城市信息
     */
    public static String getCityInfo(String ip) {
        try {
            return searcher.search(ip);
        } catch (Exception e) {
            return "未知地点";
        }
    }
}
